/*
 * Copyright (c) 2018, apexes.net. All rights reserved.
 *
 *         http://www.apexes.net
 *
 */
package net.apexes.commons.lang;

/**
 * 
 * @author <a href=mailto:hedyn@foxmail.com>HeDYn</a>
 *
 */
public final class Bytes {
    
    private Bytes() {}

    /**
     * byte数组构成int类型值
     * 与{@link #intToBytes(int)}方法互逆，即 value == byteToInt(intToByte(value)) 恒为true
     *
     * @param bytes 需要转换成int型值的byte数组，长度为4
     * @return 返回指定字节数组构成的int值
     */
    public static int bytesToInt(byte[] bytes) {
        int value = 0;
        for (int i = 0; i < 4; i++) {
            value <<= 8;
            value |= (bytes[i] & 0xff);
        }
        return value;
    }

    /**
     * int型值转换成byte数组
     * 与{@link #bytesToInt(byte[])}方法互逆，即 value == byteToInt(intToByte(value)) 恒为true
     *
     * @param value 值
     * @return 返回长度为4的字节数组
     */
    public static byte[] intToBytes(int value) {
        byte[] targets = new byte[4];
        for (int i = 0; i < 4; i++) {
            int offset = 32 - (i + 1) * 8;
            targets[i] = (byte) ((value >> offset) & 0xff);
        }
        return targets;
    }

    /**
     * byte数组构成long类型值。
     * 与{@link #longToBytes(long)}方法互逆，即 value == bytesToLong(longToBytes(value)) 恒为true
     * @param bytes 需要转换成long型值的byte数组，长度为8
     * @return 返回指定字节数组构成的long值
     */
    public static long bytesToLong(byte[] bytes) {
        long value = 0;
        for (int i = 0; i < 8; i++) {
            value <<= 8;
            value |= (bytes[i] & 0xff);
        }
        return value;
    }

    /**
     * long型值转换成byte数组。
     * 与{@link #bytesToLong(byte[])}方法互逆，即 value == bytesToLong(bytesToLong(value)) 恒为true
     * @param value 要转成字节数组的long型值
     * @return 返回长度为8的字节数组
     */
    public static byte[] longToBytes(long value) {
        byte[] targets = new byte[8];
        for (int i = 0; i < 8; i++) {
            int offset = 64 - (i + 1) * 8;
            targets[i] = (byte) ((value >> offset) & 0xff);
        }
        return targets;
    }
    
    /**
     * byte数组构成short类型值
     * 
     * @param bytes 要转成short的字节数组，长度为2
     * @return 返回指定字节数组构成的short值
     */
    public static short bytesToShort(byte[] bytes) {
        int value = (bytes[0] < 0 ? bytes[0] + 256 : bytes[0]) << 8;
        value += (bytes[1] < 0 ? bytes[1] + 256 : bytes[1]);
        return (short) value;
    }
    
    /**
     * short型转byte数组
     * 
     * @param value 要转成字节数组的short值
     * @return 返回长度为2的字节数组
     */
    public static byte[] shortToBytes(short value) {
        byte[] bytes = new byte[2];
        bytes[0] = (byte) ((value & 0xFF00) >> 8);
        bytes[1] = (byte) (value & 0xFF);
        return bytes;
    }

    /**
     * 将表示十六进制值的字符串转换为byte数组
     * 与{@link #toHex(byte[])}方法互逆
     *
     * @param hex 需要转换的十六进形式字符串
     * @return 转换后的byte数组
     */
    public static byte[] fromHex(String hex) {
        byte[] arrB = hex.getBytes();
        int iLen = arrB.length;
        // 两个字符表示一个字节，所以字节数组长度是字符串长度除以2
        byte[] arrOut = new byte[iLen / 2];
        for (int i = 0; i < iLen; i += 2) {
            String strTmp = new String(arrB, i, 2);
            arrOut[i / 2] = (byte) Integer.parseInt(strTmp, 16);
        }
        return arrOut;
    }

    /**
     * 以十六进制字符串形式显示byte数组
     * 与{@link #fromHex(String)}方法互逆
     *
     * @param bytes 字符数组
     * @return 返回字节数组的十六进制形式
     */
    public static String toHex(byte[] bytes) {
        StringBuilder builder = new StringBuilder();
        for (byte b : bytes) {
            int i = (b & 0xFF);
            if (i < 16) {
                builder.append("0");
            }
            builder.append(Integer.toHexString(i));
        }
        return builder.toString();
    }

    public static String toHex(byte value) {
        int i = (value & 0xFF);
        String hex = Integer.toHexString(i);
        if (i >= 16) {
            return hex;
        }
        return "0" + hex;
    }

    /**
     * 获取指定字节数组的HEX表示形式（每个字节用空格分隔）
     * 与{@link #hexParse(String)}方法互逆
     *
     * @param bytes 字节数组
     * @return 返回指定字节数组的HEX表示形式（每个字节用空格分隔）
     */
    public static String hexFormat(byte[] bytes) {
        StringBuilder builder = new StringBuilder();
        for (byte b : bytes) {
            int i = (b & 0xFF);
            if (i < 16) {
                builder.append("0");
            }
            builder.append(Integer.toHexString(i));
            builder.append(" ");
        }
        return builder.toString();
    }

    /**
     * 从HEX表示形式（每个字节用空格分隔）的字符串中获取字节数组
     * 与{@link #hexFormat(byte[])}方法互逆
     *
     * @param hex HEX表示形式（每个字节用空格分隔）
     * @return 返回字节数组
     */
    public static byte[] hexParse(String hex) {
        byte[] arrB = hex.getBytes();
        int iLen = arrB.length;
        // 两个字符表示一个字节，所以字节数组长度是字符串长度除以2
        byte[] arrOut = new byte[iLen / 2];
        for (int i = 0; i < iLen; i++) {
            if (arrB[i] == ' ') {
                continue;
            }
            String strTmp = new String(arrB, i, 2);
            arrOut[i / 2] = (byte) Integer.parseInt(strTmp, 16);
            i++;
        }
        return arrOut;
    }

    /**
     * 反转字节数组。
     * <pre>
     * {1, 2, 3, A, B, C} ==> {C, B, A, 3, 2, 1}
     * </pre>
     * @param array 要反转的字节数组
     * @return 返回反转后的字节数组
     */
    public static byte[] reverse(byte[] array) {
        if (array == null || array.length == 0) {
            return array;
        }
        byte[] ary = new byte[array.length];
        for (int i = 0, j = array.length-1; j >= 0; i++, j--) {
            ary[i] = array[j];
        }
        return ary;
    }

    /**
     * 判断字节数组 wholeBytes 是否以 prefixBytes 开头
     * @param wholeBytes 要判断的字节数组
     * @param prefixBytes 开头的字节
     * @return 如果是以指定字节开头则返回true
     */
    public static boolean startsWith(byte[] wholeBytes, byte[] prefixBytes) {
        if (prefixBytes.length > wholeBytes.length) {
            return false;
        }
        for (int i = 0; i < prefixBytes.length; i++) {
            if (wholeBytes[i] != prefixBytes[i]) {
                return false;
            }
        }
        return true;
    }

    /**
     * 将长度为16的字节数组转为22位Base58编码的字符串。此方法与 {@link #fromBase58Id(String)} 互逆
     * @param bytes 长度为16的字节数组
     * @return 返回长度为22字节的ID
     */
    public static String toBase58Id(byte[] bytes) {
        return IDs.toBase58Id(bytes);
    }

    /**
     * 将长度为22位的Base58字符串转为长度为16的字节数组。此方法与 {@link #toBase58Id(byte[])} 互逆
     * @param base58Id 22位的Base58字符串
     * @return 返回为长度为16的字节数组
     */
    public static byte[] fromBase58Id(String base58Id) {
        return IDs.fromBase58Id(base58Id);
    }

}
